package com.zeny.express.framework.scheduler.quartz;

import java.util.Date;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.InterruptableJob;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.quartz.PersistJobDataAfterExecution;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.util.ObjectUtils;

import com.zeny.express.framework.core.support.SpringContext;
import com.zeny.express.framework.scheduler.entity.SchedulerInfoEntity;
import com.zeny.express.framework.scheduler.entity.SchedulerLogEntity;
import com.zeny.express.framework.scheduler.service.SchedulerLogService;
import com.zeny.express.framework.shared.domain.Constants;
import com.zeny.express.framework.shared.util.InetAddressUtils;
import com.zeny.express.framework.shared.util.UUIDUtils;

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public abstract class BaseJob extends QuartzJobBean implements InterruptableJob {

	/**
	 * 日志
	 */
	protected final Logger LOGGER = LoggerFactory.getLogger(BaseJob.class);

	public static String JOB_TYPE_SIMPLE = "SIMPLE";
	
	public static String JOB_TYPE_CRON = "CRON";
	
	public static String DATA_THREAD_COUNT = "THREAD_COUNT";

	public static String DATA_THREAD_NO = "THREAD_NO";

	public static String DATA_PARAM = "PARAM";
	
	private SchedulerLogService schedulerLogService;

	protected abstract void executeJob(JobExecutionContext context)
			throws Exception;

	protected void executeInternal(JobExecutionContext context)
			throws JobExecutionException {

		// init and check
		SchedulerLogEntity entity = null;
		try {
			entity = createLogEntity(context);
			
			boolean b = isObsoleteJob(context, entity);
			
			if (b) {
				LOGGER.warn("### CLEAN OBSOLETE JOB : {}", entity.getJobId());
			}

		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
		}

		// begin
		LOGGER.info("### BEGIN EXECUTE JOB : {}", entity.getJobId());

		schedulerLogService.beginSchedulerLog(entity);

		// execute job
		try {
			executeJob(context);
			
			entity.setIsException(Constants.NO);
		} catch (Exception e) {
			entity.setIsException(Constants.YES);
			entity.setExceptionMessage(e.getMessage());

			LOGGER.error(e.getMessage(), e);
			throw new JobExecutionException(e);
		}

		// end
		entity.setIsRunning(Constants.NO);
		entity.setEndTime(new Date());
		entity.setParam(ObjectUtils.nullSafeToString(context.getJobDetail().getJobDataMap().get(DATA_PARAM)));
		schedulerLogService.endSchedulerLog(entity);

		LOGGER.info("### END EXECUTE JOB : {}", entity.getJobId());

	}

	private SchedulerLogEntity createLogEntity(JobExecutionContext context)
			throws Exception {

		if (schedulerLogService == null) {
			schedulerLogService = SpringContext
					.getBean(SchedulerLogService.class);
		}

		String schedulerId = UUIDUtils.getTimeUUID();
		String schedulerName = context.getScheduler().getSchedulerName();
		String jobId = context.getJobDetail().getKey().getName();
		String jobName = context.getJobDetail().getKey().getGroup();

		SchedulerLogEntity entity = new SchedulerLogEntity();

		entity.setId(schedulerId);
		entity.setSchedulerName(schedulerName);
		entity.setJobName(jobName);
		entity.setJobId(jobId);
		entity.setServerName(InetAddressUtils.getHostName());
		entity.setThreadId(Thread.currentThread().getName());
		entity.setBeginTime(new Date());
		entity.setIsRunning(Constants.YES);
		entity.setParam(ObjectUtils.nullSafeToString(context.getJobDetail().getJobDataMap().get(DATA_PARAM)));

		return entity;
	}
	
	private boolean isObsoleteJob(JobExecutionContext context, SchedulerLogEntity entity) throws SchedulerException {

		SchedulerInfoEntity info = schedulerLogService
				.querySchedulerInfo(entity.getJobId());
		
		if (info == null) {
			return removeJobAndTrigger(context, entity);
		} else {
			String triggerName = context.getTrigger().getKey().getName();
			if (!triggerName.equals(info.getJobId())) {
				return removeJobAndTrigger(context, entity);
			}
			
			JobDataMap data = context.getJobDetail().getJobDataMap();
			Object threadCount = data.get(DATA_THREAD_COUNT);
			
			if (!ObjectUtils.nullSafeEquals(info.getThreadCount(), threadCount)) {
				return removeJobAndTrigger(context, entity);
			}
		}
		
		return false;
	}

	private boolean removeJobAndTrigger (JobExecutionContext context, SchedulerLogEntity entity) throws SchedulerException {
		TriggerKey trigger = new TriggerKey(entity.getJobId(), entity.getJobName());
		context.getScheduler().unscheduleJob(trigger);
		JobKey job = new JobKey(entity.getJobId(), entity.getJobName());
		context.getScheduler().deleteJob(job);
		
		return true;
	}
}
